package com.ygqh.baby.handler;

import com.foxinmy.weixin4j.util.StringUtil;
import com.ygqh.baby.ao.Message;
import com.ygqh.baby.ao.OffShop;
import com.ygqh.baby.ao.TaskMethod;
import com.ygqh.baby.po.BiOrderDay;
import com.ygqh.baby.po.WdtQybOrderDetail;
import com.ygqh.baby.redis.RedisDao;
import com.ygqh.baby.service.BiOrderDayService;
import com.ygqh.baby.service.WdtQybOrderService;
import com.ygqh.baby.service.YgTaskTimeService;
import com.ygqh.baby.service.YgTaskTimeTemplate;
import com.ygqh.baby.service.task.YgBiTaskService;
import com.ygqh.baby.utils.DateConvertUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 〈订单统计〉
 *
 * @author guohao
 * @date 2019/4/15 10:10
 * @since 1.0.0
 */
@Component
public class BiOrderStatisticsHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private YgTaskTimeService ygTaskTimeService;

    @Autowired
    private BiOrderDayService biOrderDayService;

    @Autowired
    private RedisDao redisDao;


    public void statisticsOrder(String... shopNo) {
        logger.info("statisticsOrder-->开始执行 ,shopNo: {}", shopNo);

        ygTaskTimeService.execute(TaskMethod.StatisticsOrderByMinute, TaskMethod.StatisticsOrderByMinute.getCode(), new YgTaskTimeTemplate() {
            @Override
            public void execute(Date runTime) {
                Date startTime = DateConvertUtils.addMinute(runTime, -TaskMethod.StatisticsOrderByMinute.getCode());
                doStatisticsOrderByWarehouse(startTime, runTime, false, shopNo);
            }
        });
    }

    @Deprecated
    public void statisticsOrderByDay(String... shopNo) {
        logger.info("statisticsOrder-->开始执行 ,shopNo: {}", shopNo);

        ygTaskTimeService.execute(TaskMethod.StatisticsOrderByDay, TaskMethod.StatisticsOrderByDay.getCode(), new YgTaskTimeTemplate() {
            @Override
            public void execute(Date runTime) {

                Date startTime = DateConvertUtils.addDay(runTime, -TaskMethod.StatisticsOrderByDay.getCode());
                doStatisticsOrder(startTime, runTime, shopNo);
            }
        });
    }

    /**
     * 统计m站订单 （基于订单，不区分仓库）
     *
     * @param start  开始时间
     * @param end    结束时间
     * @param shopNo 店铺
     */
    @Transactional(rollbackFor = Exception.class)
    public void doStatisticsOrder(Date start, Date end, String... shopNo) {
        logger.info("statisticsOrder-->startTime:{} , endTime:{}", DateConvertUtils.formatDateTime(start), DateConvertUtils.formatDateTime(end));
        Map<String, List<BiOrderDay>> map = buildBiOrderDayMapByOrder(start, end, shopNo);
        List<BiOrderDay> newList = map.get("newList");
        List<BiOrderDay> oldList = map.get("oldList");
        biOrderDayService.addBiOrderDayBatch(newList);
        biOrderDayService.additionBatch(oldList);
    }


    /**
     * 统计m站订单 （基于订单明细,区分仓库）
     *
     * @param start  开始时间
     * @param end    结束时间
     * @param reset  是否重置
     * @param shopNo 店铺
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.NOT_SUPPORTED)
    public void doStatisticsOrderByWarehouse(Date start, Date end, boolean reset, String... shopNo) {
        Map<String, List<BiOrderDay>> mapOfOrder = buildBiOrderDayMapByOrder(start, end, shopNo);
        List<BiOrderDay> newList = mapOfOrder.get("newList");
        List<BiOrderDay> oldList = mapOfOrder.get("oldList");

        Map<String, List<BiOrderDay>> map = buildBiOrderDayMapByDetail(start, end, shopNo);
        newList.addAll(map.get("newList"));
        oldList.addAll(map.get("oldList"));
        if (reset) {
            biOrderDayService.updateBatch(oldList);
        } else {
            biOrderDayService.additionBatch(oldList);
        }
        biOrderDayService.addBiOrderDayBatch(newList);
    }

    /**
     * key: newList,oldList
     * value: List<BiOrderDay>
     *
     * @return map
     */
    private Map<String, List<BiOrderDay>> buildBiOrderDayMapByDetail(Date start, Date end, String... shopNo) {
        logger.info("statisticsOrder-->startTime:{} , endTime:{}", DateConvertUtils.formatDateTime(start), DateConvertUtils.formatDateTime(end));
        List<BiOrderDay> orderDayList = biOrderDayService.executeOrderStatisticsByDetail(start, end, shopNo);

        List<BiOrderDay> newList = new ArrayList<>();
        List<BiOrderDay> oldList = new ArrayList<>();

        if (CollectionUtils.isNotEmpty(orderDayList)) {
            for (BiOrderDay biOrderDay : orderDayList) {
                biOrderDay.setGrossMargin(getGrossMargin(biOrderDay));
                Boolean existed = existed(biOrderDay);
                biOrderDay.setUpdateTime(new Date());
                if (existed) {
                    oldList.add(biOrderDay);
                } else {
                    biOrderDay.setCreateTime(new Date());
                    newList.add(biOrderDay);
                }
            }
        }
        Map<String, List<BiOrderDay>> map = new HashMap<>(6);
        map.put("newList", newList);
        map.put("oldList", oldList);
        return map;
    }


    private Map<String, List<BiOrderDay>> buildBiOrderDayMapByOrder(Date start, Date end, String... shopNo) {
        List<BiOrderDay> orderDayList = biOrderDayService.executeOrderStatisticsNew(start, end, shopNo);
        List<BiOrderDay> newList = new ArrayList<>();
        List<BiOrderDay> oldList = new ArrayList<>();

        if (CollectionUtils.isNotEmpty(orderDayList)) {
            List<BiOrderDay> grossProfitList = biOrderDayService.executeOrderDateGrossProfitNew(start, end, shopNo);

            Map<String, BiOrderDay> grossProfitMap = grossProfitList.stream().collect(Collectors.toMap(BiOrderDay::getKey, Function.identity()));
            for (BiOrderDay od : orderDayList) {
                od.setWarehouseNo("0");
                od.setUpdateTime(new Date());
                BiOrderDay grossProfit = grossProfitMap.get(od.getKey());
                if (grossProfit != null) {
                    od.setTotalCostPrice(grossProfit.getTotalCostPrice());
                    od.setGrossMargin(getGrossMargin(grossProfit));
                } else {
                    od.setTotalCostPrice(BigDecimal.ZERO);
                    od.setGrossMargin(BigDecimal.ZERO);
                }
                Boolean existed = existed(od);
                if (existed) {
                    od.setCreateTime(new Date());
                    oldList.add(od);
                } else {
                    newList.add(od);
                }
            }
        }
        Map<String, List<BiOrderDay>> map = new HashMap<>();
        map.put("newList", newList);
        map.put("oldList", oldList);
        return map;
    }

    private Boolean existed(BiOrderDay od) {

        Boolean existed;
        String key = BI_ORDER_DAY_CACHE_KEY + ":" + od.getKey();
        String cacheRes = redisDao.get(key);
        if (cacheRes == null) {
            existed = getAndSetCache(od);
        } else {
            existed = Boolean.valueOf(cacheRes);
        }
        return existed;

    }

    private final static String BI_ORDER_DAY_CACHE_KEY = "BI_ORDER_DAY_CACHE";

    private synchronized Boolean getAndSetCache(BiOrderDay od) {
        boolean existed = false;
        String key = BI_ORDER_DAY_CACHE_KEY + ":" + od.getKey();
        String cacheRes = redisDao.get(key);
        if (cacheRes == null) {
            BiOrderDay byShop = biOrderDayService.findByShop(od.getShopType(), od.getOrderDate(), od.getWarehouseNo());
            if (byShop != null) {
                existed = true;
                redisDao.set(key, Boolean.TRUE.toString(), 25 * 60 * 60 * 1000);
            }
        } else {
            existed = true;
        }
        return existed;
    }

    public Set<String> delCache() {
        Set<String> keysStr = redisDao.keysStr(BI_ORDER_DAY_CACHE_KEY + "*");
        redisDao.del(keysStr.toArray(new String[0]));
        return keysStr;
    }

    private BigDecimal getGrossMargin(BiOrderDay orderDay) {
        if (orderDay == null || orderDay.getPayOrderPrice() == null || orderDay.getTotalCostPrice() == null
                || orderDay.getPayOrderPrice().compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;

        }
        if (StringUtil.isBlank(orderDay.getWarehouseNo())) {
            orderDay.setWarehouseNo("009");
        }
        return (orderDay.getPayOrderPrice().subtract(orderDay.getTotalCostPrice())).divide(orderDay.getPayOrderPrice(), 2, BigDecimal.ROUND_HALF_UP);
    }

    @Autowired
    private YgBiTaskService ygBiTaskService;
    @Autowired
    private WdtQybOrderService wdtQybOrderService;
    @Autowired
    private OffShopBiOrderHandler offShopBiOrderHandler;

    public Message resetBiOrderDay(String shopNo, Date startDate, Date endDate) {
        Assert.notNull(shopNo, "shopNo can't be null");
        Assert.notNull(startDate, "startDate can't be null");
        Assert.notNull(endDate, "endDate can't be null");
        endDate = DateConvertUtils.getDateEnd(endDate);
        switch (shopNo) {
            case "All": {
                doStatisticsOrderByWarehouse(startDate, endDate, true, null);
                offShopBiOrderHandler.resetBiOrder(startDate, endDate, OffShop.Kids);
                offShopBiOrderHandler.resetBiOrder(startDate, endDate, OffShop.TaiYangGong);
                resetWdtOrderDay(null, startDate, endDate);
                break;
            }
            case "M000":
            case "M001":
                doStatisticsOrderByWarehouse(startDate, endDate, true, shopNo);
                break;
            case "M002":
                offShopBiOrderHandler.resetBiOrder(startDate, endDate, OffShop.Kids);
                break;
            case "TaiYangGong":
                offShopBiOrderHandler.resetBiOrder(startDate, endDate, OffShop.TaiYangGong);
                break;
            default: {
                resetWdtOrderDay(shopNo, startDate, endDate);
                break;
            }
        }
        return Message.success(null);

    }

    private void resetWdtOrderDay(String shopNo, Date startDate, Date endDate) {
        List<WdtQybOrderDetail> detailListByTime = wdtQybOrderService.findDetailListByTime(startDate, endDate, shopNo);
        Map<String, List<BiOrderDay>> wdtMap = ygBiTaskService.buildBiOrderDayMap(detailListByTime);
        List<BiOrderDay> newList = wdtMap.get("newList");
        List<BiOrderDay> oldList = wdtMap.get("oldList");
        biOrderDayService.addBiOrderDayBatch(newList);
        biOrderDayService.updateBatch(oldList);
    }

}
